﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IsreversI
{
    /// <summary>
    /// Convenience struct for storing the state of a tile.
    /// </summary>
    /// <remarks>
    /// Warning!  Boilerplate ahead!
    /// </remarks>
    struct TileState
    {
        private enum State
        {
            red = 1,
            blue = 2,
            empty = 4,
            movepossible = 8,
            invalid = 128 // something suitably large and unlikely to occur at random
        }

        #region member variables
        private State state;
        #endregion

        #region static instances
        public static readonly TileState Red = new TileState(State.red);
        public static readonly TileState Blue = new TileState(State.blue);
        public static readonly TileState Empty = new TileState(State.empty);
        public static readonly TileState MovePossible = new TileState(State.movepossible);
        public static readonly TileState Invalid = new TileState(State.invalid);
        #endregion

        #region constructors
        private TileState(State state)
        {
            this.state = state;
        }

        private TileState(int state)
        {
            this.state = (TileState.State)state;
        }
        #endregion

        #region public functions
        public bool HasFlag(TileState o)
        {
            return this.state.HasFlag(o.state);
        }

        public TileState Flipped()
        {
            if (this == Red)
                return Blue;
            else if (this == Blue)
                return Red;
            Debug.Assert(false, "Cannot invert state " + state);
            return Invalid;
        }

        public PlayerColour OfPlayer()
        {
            if (this.HasFlag(TileState.Red))
                return PlayerColour.Red;
            else if (this.HasFlag(TileState.Blue))
                return PlayerColour.Blue;
            return PlayerColour.Invalid;
        }

        public bool Equals(TileState s)
        {
            return this.state == s.state;
        }

        public bool IsEmpty()
        {
            return this == Empty;
        }

        public TileState WithSet(TileState s)
        {
            return new TileState(this.state | s.state);
        }

        public TileState WithUnset(TileState s)
        {
            return new TileState(this.state & ~s.state);
        }
        #endregion

        #region overides
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            TileState s = (TileState)obj;
            return this.state == s.state;
        }

        public override int GetHashCode()
        {
            return (int)this.state;
        }

        public override string ToString()
        {
            switch (this.state)
            {
                case State.red:
                    return "Red";
                case State.blue:
                    return "Blue";
                case State.empty:
                    return "Empty";
                case State.movepossible:
                    return "Move Possible";
                case (State)0:
                    return "Null";
                default:
                    return "Invalid";
            }
        }
        #endregion

        #region operator overloads
        static public TileState operator ~(TileState state)
        {
            return ~state;
        }

        static public bool operator ==(TileState f, TileState s)
        {
            return f.Equals(s);
        }

        static public bool operator !=(TileState f, TileState s)
        {
            return !f.Equals(s);
        }

        static public TileState operator |(TileState f, TileState s)
        {
            return new TileState(f.state | s.state);
        }

        static public TileState operator &(TileState f, TileState s)
        {
            return new TileState(f.state & s.state);
        }
        #endregion
    }
}
